package com.yjzx.util.generate;

import com.google.auto.service.AutoService;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author guozhi
 * @date 2024/10/24
 * @description EnumByFieldGenerator
 */
@AutoService(Processor.class)
@SupportedAnnotationTypes("com.yjzx.util.generate.EnumGenerateByFieldMethod")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class EnumByFieldGenerator extends AbstractProcessor {
    // 编译时期输入日志的
    private Messager messager;

    // 将Element转换为JCTree的工具,提供了待处理的抽象语法树
    private JavacTrees trees;

    // 封装了创建AST节点的一些方法
    private TreeMaker treeMaker;

    // 提供了创建标识符的方法
    private Names names;

    private Name name;


    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        // 获取被@Getter注解标记的所有元素(这个元素可能是类、变量、方法等等)
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(EnumGenerateByFieldMethod.class);

        set.forEach(element -> {
            TypeElement enclosingClass = (TypeElement) element.getEnclosingElement();
            if (enclosingClass.getKind() == ElementKind.ENUM) {
                java.util.List<? extends Element> elements = enclosingClass.getEnclosedElements();
                java.util.List<JCTree.JCVariableDecl> list = elements.stream()
                        .filter(x -> x.getKind() == ElementKind.ENUM_CONSTANT)
                        .map(x -> {
                            Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) x;
                            return treeMaker.VarDef(varSymbol, null);
                        }).collect(Collectors.toList());


                // 将Element转换为JCTree
                JCTree jcTree = trees.getTree(element);
                JCTree.JCClassDecl jcClassDecl = trees.getTree(enclosingClass);
                name = jcClassDecl.name;
                EnumGenerateByFieldMethod annotation = element.getAnnotation(EnumGenerateByFieldMethod.class);
                DefaultValueEnum valueEnum = annotation.defaultValue();
                String fieldName = element.getSimpleName().toString();
                jcTree.accept(new TreeTranslator() {
                    @Override
                    public void visitVarDef(JCTree.JCVariableDecl jcVariableDecl) {
                        super.visitVarDef(jcVariableDecl);
                        String methodName = annotation.methodName().replace("${fieldName}", fieldName);
                        messager.printMessage(Diagnostic.Kind.NOTE, methodName + " has been processed");
                        treeMaker.pos = jcVariableDecl.pos;
                        //类里的前面追加生成的Getter方法

                        JCTree.JCMethodDecl methodDecl = makeMethodDecl(jcVariableDecl, list, valueEnum, methodName, fieldName);
                        if (methodDecl != null) {
                            jcClassDecl.defs = jcClassDecl.defs.append(methodDecl);

                        }


                    }

                    /***
                     * JCTree.Visitor有很多方法，我们可以通过重写对应的方法,(从该方法的形参中)来获取到我们想要的信息:
                     * 如: 重写visitClassDef方法， 获取到类的信息;
                     *     重写visitMethodDef方法， 获取到方法的信息;
                     *     重写visitVarDef方法， 获取到变量的信息;
                     * @param jcClassDecl
                     */

                    /*@Override
                    public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {

                        //创建一个变量语法树节点的List
                        List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();

                        // 遍历defs，获取到类里的所有变量
                        for (JCTree tree : jcClassDecl.defs) {
                            if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                                JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                                jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                            }
                        }
                        // 对于变量进行生成方法的操作
                        jcVariableDeclList.forEach(jcVariableDecl -> {
                            String methodName = annotation.methodName().replace("${fieldName}", fieldName);
                            messager.printMessage(Diagnostic.Kind.NOTE, methodName + " has been processed");
                            treeMaker.pos = jcVariableDecl.pos;
                            //类里的前面追加生成的Getter方法
                            JCTree.JCMethodDecl methodDecl = makeMethodDecl(jcClassDecl, jcVariableDecl, list, valueEnum, methodName);
                            jcClassDecl.defs = methodDecl == null ? jcClassDecl.defs : jcClassDecl.defs.append(methodDecl);
                        });
                        super.visitClassDef(jcClassDecl);
                    }*/

                });
            }

        });
        System.out.println(roundEnv);
        //我们有修改过AST，所以返回true
        return true;
    }

    private JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl, java.util.List<JCTree.JCVariableDecl> list, DefaultValueEnum valueEnum, String methodName, String fieldName) {
        /***
         * JCStatement：声明语法树节点，常见的子类如下
         * JCBlock：语句块语法树节点
         * JCReturn：return语句语法树节点
         * JCClassDecl：类定义语法树节点
         * JCVariableDecl：字段/变量定义语法树节点
         * JCMethodDecl：方法定义语法树节点
         * JCModifiers：访问标志语法树节点
         * JCExpression：表达式语法树节点，常见的子类如下
         * JCAssign：赋值语句语法树节点
         * JCIdent：标识符语法树节点，可以是变量，类型，关键字等等
         */

        int length = list.size();
        int index = length - 1;
        JCTree.JCIf ifStatement = null;
        JCTree.JCExpression condition;

        HashMap<JCTree.JCVariableDecl, JCTree.JCBlock> map = new HashMap<>();
        JCTree.JCBlock elseBlock = null;
        ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();
        for (int i = index; i >= 0; i--) {
            JCTree.JCVariableDecl decl = list.get(i);


            JCTree.JCBlock newTrueBlock = treeMaker.Block(0, List.of(treeMaker.Return(treeMaker.Select(treeMaker.Ident(this.name), decl.name))));
            map.put(decl, newTrueBlock);
            if ((DefaultValueEnum.FIRST_VALUE == valueEnum && i == 0)
                    || (DefaultValueEnum.LAST_VALUE == valueEnum && i == length - 1)) {
                elseBlock = newTrueBlock;
            }
        }
        JCTree.JCVariableDecl param1 = treeMaker.VarDef(
                treeMaker.Modifiers(0),
                names.fromString("param1"),
                jcVariableDecl.vartype,
                null);

        if (DefaultValueEnum.ERROR_VALUE == valueEnum) {
            elseBlock = treeMaker.Block(0, List.of(treeMaker.Throw(treeMaker.NewClass(null, List.nil(), treeMaker.Ident(names.fromString("RuntimeException")), List.of(treeMaker.Literal("参数错误")), null))));
        } else if (elseBlock == null) {
            elseBlock = treeMaker.Block(0, List.of(treeMaker.Return(treeMaker.Literal(null))));
        }
        Map.Entry<JCTree.JCVariableDecl, JCTree.JCBlock> v1 = null;
        for (Map.Entry<JCTree.JCVariableDecl, JCTree.JCBlock> entry : map.entrySet()) {
            condition = treeMaker.Binary(JCTree.Tag.EQ,
                    treeMaker.Select(treeMaker.Select(treeMaker.Ident(this.name), entry.getKey().name), names.fromString("code"))
                    , treeMaker.Ident(names.fromString("param1"))
            );
            if (v1 == null) {
                v1 = entry;
                ifStatement = treeMaker.If(condition, entry.getValue(), elseBlock);
            } else {
                ifStatement = treeMaker.If(condition, entry.getValue(), ifStatement);
            }
        }
        JCTree.JCBlock body = treeMaker.Block(0, List.of(ifStatement));
        Name name = names.fromString(methodName);
        return treeMaker.MethodDef(
                //mods：访问标志
                treeMaker.Modifiers(Flags.STATIC | Flags.PUBLIC),
                //name：方法名
                name,
                //restype：返回类型
                treeMaker.Ident(this.name),
                //typarams：泛型参数列表
                List.nil(),
                //params：参数列表
                List.of(param1),
                //thrown：异常声明列表
                List.nil(),
                //方法体
                body,
                null);

    }

    private Name getNewMethodName(Name name) {
        String s = name.toString();
        return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
    }

}
